/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
 *
 */

import cluster, { Worker } from 'cluster';

import { levels } from 'log4js';

import { evaluateLogLevel } from '../../log/hvigor-log.js';
import { configureDaemon, HvigorDaemonLogger } from '../log/hvigor-daemon-log.js';
import { registryProcessListener, setProcessRemovedFromRegistryTimer, setProcessTimeOutTimer } from '../process/daemon-process-lifecycle.js';
import { DaemonServerLifecycle } from '../server/daemon-server-lifecycle.js';
import { DaemonServerListenerRegistry } from '../server/daemon-server-listener-registry.js';
import { HvigorDaemonServer } from '../server/hvigor-daemon-server.js';
import { SessionIdHelper } from '../session/session-id.js';
import { calcChildExecArgv } from '../../../common/util/cacl-exec-argv-util.js';
import process from 'process';

const _log = HvigorDaemonLogger.getLogger('daemon');
export function masterProcessBoot() {
  if (cluster.isPrimary) {
    const logLevelByProcessArgv = process.env._logLevel ?? levels.INFO;

    // 在确定使用daemon的情况下，在这里初始化一次daemonClientLogger
    configureDaemon();
    const level = levels.getLevel(logLevelByProcessArgv);
    if (level !== undefined) {
      evaluateLogLevel(level, ['daemon']);
    }
    _log.debug(`hvigor daemon: Set daemon process log level, logLevel=${logLevelByProcessArgv}`);

    if (!process.env._sessionId) {
      _log.errorMessageExit('hvigor daemon: Session id cannot be empty. Daemon process will exit.');
    }

    const sessionId = SessionIdHelper.decryptPwdByDefaultMaterial(process.env._sessionId!, _log);

    // 校验过sessionId后，cluster初始化，新建一个worker进程
    initCluster(_log);

    const hvigorDaemonServer = HvigorDaemonServer.getInstance(sessionId);
    registryProcessListener(hvigorDaemonServer);

    const daemonServerLifecycle = new DaemonServerLifecycle();
    const daemonServerListenerRegistry = new DaemonServerListenerRegistry();

    // 为server对象添加所需监听事件
    daemonServerListenerRegistry.registryAllListener(hvigorDaemonServer);

    // 启动server对端口的监听，并在开始监听后，添加process的心跳检测与自动消亡倒计时
    daemonServerLifecycle.startListen(hvigorDaemonServer).then(() => {
      setProcessTimeOutTimer(hvigorDaemonServer);
      setProcessRemovedFromRegistryTimer(hvigorDaemonServer);
    });
  }
}

function newWorkerProcess(trigByConfigChange=false) {
  process.execArgv = calcChildExecArgv(trigByConfigChange);

  _log.debug(`daemon, use the parent process.execArgv ${process.execArgv}`);
  cluster.setupPrimary({
    execArgv: process.execArgv,
  });
  const worker = cluster.fork();
  worker.process.stdout!.pipe(process.stdout);
  worker.process.stderr!.pipe(process.stderr);
}

/**
 * 检测是否由hvigor_config文件变化触发worker主动退出
 * @param {Worker} worker
 * @return {boolean} - 是否由configChange触发
 */
function trigByConfigChange(worker: Worker):boolean{
  // 目前只有在hvigor_config修改触发重启时才会返回True
  return worker.exitedAfterDisconnect;
}

function initCluster(log: HvigorDaemonLogger) {
  cluster.on('setup', (option) => {
    log.debug('hvigor daemon: worker process fork option:');
    log.debug(option);
  });
  cluster.on('exit', (worker, code, signal) => {
    log.debug(`hvigor daemon: worker ${worker.process.pid} died (${signal || code}).`);
    log.debug(`hvigor daemon: worker ${worker.process.pid} exited with code ${code} and signal ${signal}`);

    // 检查是否是因为异常退出
    if (worker.exitedAfterDisconnect) {
      log.debug(`hvigor daemon: worker ${worker.process.pid} requested restart proactively.`);
    }
    if (!cluster.workers || Object.keys(cluster.workers).length === 0) {
      // 在没有worker进程的时候新建进程
      log.debug('hvigor daemon: no worker alive. Start a new worker process.');
      newWorkerProcess(trigByConfigChange(worker));
    }
  });
  cluster.on('online', (worker) => {
    log.debug(`hvigor daemon: worker ${worker.process.pid} with worker id ${worker.id} is online.`);
    worker.on('disconnect', () => {
      log.debug(`hvigor daemon: worker ${worker.process.pid} with worker id ${worker.id} is disconnected.`);
    });
    worker.on('exit', (code) => {
      log.debug(`hvigor daemon: worker ${worker.process.pid} with worker id ${worker.id} exit with code ${code}.`);
    });
    worker.on('error', (error) => {
      log.debug(`hvigor daemon: worker ${worker.process.pid} with worker id ${worker.id} has an error: ${error}`);
      if (error.stack) {
        log.debug(error.stack);
      }
    });
  });
  if (!cluster.workers || Object.keys(cluster.workers).length === 0) {
    cluster.setupPrimary({
      windowsHide: true,
      silent: true,
    });
    newWorkerProcess();
  }
}
